
package xj.graph2d.app.diagram;

import java.util.HashMap;
import java.util.Map;

import xj.graph2d.Document;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.DrawShapeFactory;
import xj.graph2d.GraphEdgeBase;
import xj.graph2d.ModelListener;
import xj.graph2d.app.DrawApp;
import xj.graph2d.layout.ClassDiagramLayout;
import xj.graph2d.shapes.uml.ClassShape;
import xj.graph2d.shapes.GraphEdgeShape;
import xj.graph2d.template.UMLTemplate;
import xj.meta.ModelElement;
import xj.model.uml.Multiplicity;
import xj.model.uml.UMLAssociation;

public class ClassDiagramBuilder 
  extends DiagramBuilder { 

  public ClassDiagramBuilder(DrawApp main, String name) {
    super(main, name, Document.DocumentType.UML_Class_Diagram);

    diagram.setGraphLayoutManager(new ClassDiagramLayout(diagram));    
  }

  public static ClassShape makeClassShape(String name, 
					  boolean isInterface, 
					  boolean isAbstract) {
    return makeClassShape(name, null, null, isInterface, isAbstract);
  }

  public static ClassShape makeClassShape(String name, 
					  String gentype, 
					  UMLTemplate template,
					  boolean isInterface, 
					  boolean isAbstract) {
    ClassShape cs = (ClassShape) makeShape("UMLClassDiagram.Class2");
    cs.setClassName(name, isInterface, isAbstract);
    if (gentype != null && template != null) { 
      DrawShape tshape = cs.findTemplateShape();
      if (tshape == null) {
	tshape = template.makeGenericTemplateShape(60, 20);
	cs.setTemplateShape(tshape);
      }
      cs.setTemplates(gentype, true);
    }
    cs.fitCompartments(true);
    cs.doLayout();
    return cs;
  }

  public static GraphEdgeBase makeGeneralizationEdge(boolean extention) {
    String tempname;
    if (extention) { 
      tempname = "UMLClassDiagram.Generalization";
    } else { 
      tempname = "UMLClassDiagram.Realization";
    }
    GraphEdgeBase edge = (GraphEdgeBase) makeShape(tempname); 
    if (extention) { 
      edge.setEdgeType(UMLTemplate.GENERALIZATION_EDGE);
    } else { 
      edge.setEdgeType(UMLTemplate.REALIZATION_EDGE);
    }
    return edge;
  }

  public static GraphEdgeBase makeAssociationEdge(String assocname, 
						  boolean aggregation, boolean composition, 
						  String role1, Multiplicity multiplicity1,
						  String role2, Multiplicity multiplicity2) {  
    return makeAssociationEdge(assocname, false, aggregation, composition, 
			       role1, multiplicity1, role2, multiplicity2);
  }

  public static GraphEdgeBase makeAssociationEdge(String assocname, 
						  boolean self, 
						  boolean aggregation, boolean composition, 
						  String role1, Multiplicity multiplicity1,
						  String role2, Multiplicity multiplicity2) {  
    String tempname;
    if (self) { 
      tempname = "UMLClassDiagram.Association4";
    } else { 
      if (aggregation) { 
	if (composition) { 
	  tempname = "UMLClassDiagram.Composition";
	} else { 
	  tempname = "UMLClassDiagram.Aggregation";
	}
      } else { 
	tempname = "UMLClassDiagram.Association1";
      }
    }
    GraphEdgeBase edge = (GraphEdgeBase) makeShape(tempname); 
    edge.setEdgeType(UMLTemplate.ASSOCIATION_EDGE);

    //System.out.println("ClassDiagramBuilder.makeAssociationEdge() tempname = " + tempname + 
    //	       " edge: " + edge.getClass().getName());

    if (assocname != null) { 
      ModelElement model = edge.getModel();
      if (model instanceof UMLAssociation) { 
	UMLAssociation amodel = (UMLAssociation) model;
	amodel.setName(assocname);
	if (role1 != null) { 
	  amodel.setLeftRole(role1);
	}
	if (multiplicity1 != null) { 
	  amodel.setLeftMultiplicity(multiplicity1);
	}
	
	if (role2 != null) { 
	  amodel.setRightRole(role2);
	}
	if (multiplicity2 != null) { 
	  amodel.setRightMultiplicity(multiplicity2);
	}
	edge.updateShape();
      }
    }

    if (self && 
	edge instanceof GraphEdgeShape) { 
      //System.out.println("ClassDiagramBuilder.makeAssociationEdge() self loop");      
      GraphEdgeShape e = (GraphEdgeShape) edge; 
      e.setAdjustable(false);
    }

    /*
    if (edge instanceof OrthogonalSelfLoop4) { 
      System.out.println("ClassDiagramBuilder.makeAssociationEdge() self loop");

      OrthogonalSelfLoop4 selfloop = (OrthogonalSelfLoop4) edge;
      //selfloop.adjustSelfLoop();
      selfloop.setAdjustable(false);
    }
    */
    
    return edge;
  }

  public ClassShape addClass(String name) { 
    return addClass(name, false, false);
  }

  public ClassShape addClass(String name, 
			     boolean isInterface, 
			     boolean isAbstract) {
    if (diagram != null) { 
      ClassShape cs = makeClassShape(name, isInterface, isAbstract);
      diagram.addNode(cs);
      classMap.put(name, cs);
      return cs; 
    }
    return null;
  }

  public void addExtention(String subclassname, String superclassname) { 
    addGeneralization(subclassname, superclassname, true); 
  }

  public void addRealization(String subclassname, String superclassname) { 
    addGeneralization(subclassname, superclassname, false); 
  }			    

  public void addGeneralization(String subclassname, 
				String superclassname, 
				boolean extention) {
    if (subclassname != null && 
	superclassname != null) { 
      ClassShape superclass = classMap.get(superclassname); 
      ClassShape subclass = classMap.get(subclassname); 
      if (superclass != null && 
	  subclass != null) { 
	GraphEdgeBase edge = makeGeneralizationEdge(extention); 	
	addEdge(edge, subclass, superclass);	    
      }
    }
  }

  public void addAssociation(String classname1, String classname2) {  
    addAssociation(classname1, classname2, null);
  }

  public void addAssociation(String classname1, String classname2,
			     String assocname) {  
    addAssociation(classname1, classname2, assocname, false, false, null, null, null, null);
  }

  public void addAssociation(String classname1, String classname2,
			     String assocname, 
			     String role1, Multiplicity multiplicity1,
			     String role2, Multiplicity multiplicity2) {  
    addAssociation(classname1, classname2, 
		   assocname, false, false, 
		   role1, multiplicity1, 
		   role2, multiplicity2);
  }

  public void addAssociation(String classname1, String classname2,
			     String assocname, 
			     boolean aggregation, boolean composition) { 
    addAssociation(classname1, classname2, assocname, 
		   aggregation, composition, 
		   null, null, null, null);
  }

  public void addAssociation(String classname1, String classname2,
			     String assocname, 
			     boolean aggregation, boolean composition, 
			     String role1, Multiplicity multiplicity1, 
			     String role2, Multiplicity multiplicity2) {  
    if (classname1 != null && 
	classname2 != null) { 
      ClassShape class1 = classMap.get(classname1); 
      ClassShape class2 = classMap.get(classname2); 
      if (class1 != null && 
	  class2 != null) { 
	GraphEdgeBase edge = makeAssociationEdge(assocname, aggregation, composition, 
						 role1, multiplicity1, role2, multiplicity2);
	addEdge(edge, class1, class2); 
      }
    }
  }

  public void clearDiagram(){
    classMap.clear();
    diagram.removeAllNodes();
    diagram.removeAll();
  }

  protected Map<String, ClassShape> classMap = new HashMap<String, ClassShape>();

}

